package com.trinity.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import com.trinity.common.constant.Constants;
import com.trinity.common.constant.UserConstants;
import com.trinity.common.core.domain.AjaxResult;
import com.trinity.common.core.domain.dto.StudentUser;
import com.trinity.common.core.domain.entity.SysUser;
import com.trinity.common.core.domain.entity.WxUser;
import com.trinity.common.core.domain.model.LoginUser;
import com.trinity.common.exception.ServiceException;
import com.trinity.common.exception.user.BlackListException;
import com.trinity.common.exception.user.UserNotExistsException;
import com.trinity.common.exception.user.UserPasswordNotMatchException;
import com.trinity.common.utils.DateUtils;
import com.trinity.common.utils.MessageUtils;
import com.trinity.common.utils.SecurityUtils;
import com.trinity.common.utils.StringUtils;
import com.trinity.common.utils.bean.BeanUtils;
import com.trinity.common.utils.ip.IpUtils;
import com.trinity.framework.manager.AsyncManager;
import com.trinity.framework.manager.factory.AsyncFactory;
import com.trinity.framework.security.context.AuthenticationContextHolder;
import com.trinity.framework.web.service.TokenService;
import com.trinity.mapper.WxUserMapper;
import com.trinity.service.IWxUserService;
import com.trinity.system.service.ISysConfigService;
import com.trinity.system.service.ISysUserService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 微信用户Service业务层处理
 *
 * @author trinity
 * @date 2023-07-24
 */
@Service
@Slf4j
public class WxUserServiceImpl implements IWxUserService {
    @Resource
    private WxUserMapper wxUserMapper;
    @Resource
    private TokenService tokenService;

    @Resource
    private ISysUserService userService;
    @Resource
    private WxMaService wxMaService;
    @Resource
    private ISysConfigService configService;
    @Resource
    private AuthenticationManager authenticationManager;

    /**
     * 查询微信用户
     *
     * @param userId 微信用户主键
     * @return 微信用户
     */
    @Override
    public WxUser selectWxUserByUserId(Long userId) {
        return wxUserMapper.selectWxUserByUserId(userId);
    }

    /**
     * 查询微信用户
     *
     * @param openId openId
     * @return 微信用户
     */
    @Override
    public WxUser selectWxUserByOpenId(String openId) {
        return wxUserMapper.selectWxUserByOpenId(openId);
    }

    /**
     * 查询微信用户列表
     *
     * @param wxUser 微信用户
     * @return 微信用户
     */
    @Override
    public List<WxUser> selectWxUserList(WxUser wxUser) {
        return wxUserMapper.selectWxUserList(wxUser);
    }

    /**
     * 新增微信用户
     *
     * @param wxUser 微信用户
     * @return 结果
     */
    @Override
    public int insertWxUser(WxUser wxUser) {
        return wxUserMapper.insertWxUser(wxUser);
    }

    /**
     * 修改微信用户
     *
     * @param wxUser 微信用户
     * @return 结果
     */
    @Override
    public int updateWxUser(WxUser wxUser) {
        return wxUserMapper.updateWxUser(wxUser);
    }

    /**
     * 批量删除微信用户
     *
     * @param userIds 需要删除的微信用户主键
     * @return 结果
     */
    @Override
    public int deleteWxUserByUserIds(Long[] userIds) {
        return wxUserMapper.deleteWxUserByUserIds(userIds);
    }

    /**
     * 删除微信用户信息
     *
     * @param userId 微信用户主键
     * @return 结果
     */
    @Override
    public int deleteWxUserByUserId(Long userId) {
        return wxUserMapper.deleteWxUserByUserId(userId);
    }


    /**
     * 根据手机号码判断用户是否存在
     *
     * @param phone 手机号码
     * @return 结果
     */
    @Override
    public boolean checkUserByPhone(String phone) {
        SysUser info = userService.selectUserByPhone(phone);
        return StringUtils.isNotNull(info);
    }

    /**
     * 校验手机号码是已经绑定微信用户
     *
     * @param phone 手机号码
     * @return 结果
     */
    @Override
    public boolean checkPhoneIsBundled(String phone) {
        SysUser info = userService.selectUserByPhone(phone);
        if (StringUtils.isNotNull(info)) {
            WxUser wxUser = selectWxUserByUserId(info.getUserId());
            return StringUtils.isNotNull(wxUser);
        }
        return UserConstants.NOT_UNIQUE;
    }

    /**
     * 绑定微信用户
     *
     * @param phone  phone
     * @param openid openid
     */
    @Override
    public void bundledWxUser(String phone, String openid) {
        SysUser user = userService.selectUserByPhone(phone);
        if (StringUtils.isNotNull(user)) {
            WxUser wxUser = new WxUser();
            wxUser.setUserId(user.getUserId());
            wxUser.setOpenId(openid);
            wxUserMapper.insertWxUser(wxUser);

        }
    }

    public void selectStudentUser() {
        //openid 唯一
        //studentId 唯一
        //username 唯一
        //userId 唯一

    }

    /**
     * 绑定微信用户
     *
     * @param openid openid
     * @return 结果
     */
    @Override
    public String createToken(String openid, String sessionKey) {
        SysUser user = userService.selectUserById(wxUserMapper.selectWxUserByOpenId(openid).getUserId());
        if (StringUtils.isNotNull(user)) {
            //  发放token
            //组装token信息
            LoginUser loginUser = new LoginUser();
            loginUser.setWxUser(WxUser.builder().openId(openid).sessionKey(sessionKey).build());
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(openid, sessionKey);
            authenticationToken.setDetails(loginUser);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            // 生成token
            return tokenService.createToken(loginUser);
        } else {
            ;
            return null;
        }
    }


    /**
     * 获取微信用户openid
     *
     * @param appid appid
     * @param code  code
     * @return 结果
     */
    @SneakyThrows
    @Override
    public WxMaJscode2SessionResult getSession(String appid, String code) {
        try {
            WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(code);
//            return JsonUtils.toJson(session);
            return session;
        } catch (WxErrorException e) {
            throw new WxErrorException(e.toString());
        } finally {
            WxMaConfigHolder.remove();//清理ThreadLocal
        }

    }

    /**
     * 注册
     */
    public boolean register(String openId, String phone, String password) {
        boolean regFlag = false;
        String msg = "";
        SysUser sysUser = new SysUser();
        sysUser.setUserName(openId);
        if (StringUtils.isEmpty(openId)) {
            msg = "用户名不能为空";
        } else if (StringUtils.isEmpty(password)) {
            msg = "用户密码不能为空";
        } else if (!userService.checkUserNameUnique(sysUser)) {
            msg = "保存用户'" + openId + "'失败，注册账号已存在";
        } else {
            sysUser.setNickName(openId);
            sysUser.setPassword(SecurityUtils.encryptPassword(password));
            if (isValidPhoneNumber(phone)) {
                sysUser.setPhonenumber(phone);
            }
            regFlag = userService.registerUser(sysUser);
        }
        if (regFlag) {
            // 注册用户后和openid进行绑定
            WxUser wxUser = new WxUser();
            wxUser.setUserId(sysUser.getUserId());
            wxUser.setOpenId(openId);
            int i = insertWxUser(wxUser);
        }
        return regFlag;
    }

    public boolean isValidPhoneNumber(String phoneNumber) {
        // 定义中国大陆手机号码的正则表达式
        String regex = "^1[3-9]\\d{9}$";

        // 创建 Pattern 对象
        Pattern pattern = Pattern.compile(regex);

        // 创建 Matcher 对象
        Matcher matcher = pattern.matcher(phoneNumber);

        // 进行匹配并返回结果
        return matcher.matches();
    }


    @Override
    public AjaxResult wxLongByAppidAndCode(String appid, String code, String phone) throws WxErrorException {
        AjaxResult ajax = AjaxResult.success();
        WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
        String openid = sessionInfo.getOpenid();
        String sessionKey = sessionInfo.getSessionKey();
        String password = configService.selectConfigByKey("sys.user.initPassword");
        WxUser wxUser = selectWxUserByOpenId(openid);
        // 新用户先进行注册
        if (Objects.isNull(wxUser)) {
            boolean register = register(openid, phone, password);
            if (!register) {
                return AjaxResult.error("新增用户失败,openid:'" + openid);
            }
            //注册成功
            wxUser = selectWxUserByOpenId(openid);
        }
        Long userId = wxUser.getUserId();
        //已注册用户检查手机号绑定
        SysUser sysUser = userService.selectUserById(userId);
        if (sysUser != null && StringUtils.isBlank(sysUser.getPhonenumber())) {
            String regex = "^1[3-9]\\d{9}$";
            // 创建 Pattern 对象
            Pattern pattern = Pattern.compile(regex);
            // 创建 Matcher 对象
            Matcher matcher = pattern.matcher(phone);
            // 进行匹配并返回结果
            if (!matcher.matches()) {
                ajax.put("code", "1000000401");
                ajax.put("msg", "手机号不正确");
                return ajax;
            }
            //携带手机号 需要进行绑定
            sysUser.setPhonenumber(phone);
            if (StringUtils.isNotEmpty(sysUser.getPhonenumber()) && !userService.checkPhoneUnique(sysUser)) {
                return AjaxResult.error("修改用户'" + sysUser.getUserName() + "'失败，手机号码已存在");
            }
            //用户未绑定手机号 携带手机号进行重新登录
            if (userService.updateUserProfile(sysUser) < 0 || StringUtils.isBlank(phone)) {
                ajax.put("code", "1000000401");
                ajax.put("msg", "请绑定手机号");
                return ajax;
            }
        }
        //用户已经绑定了openid直接使用openid进行登录 // 生成令牌
        String token = loginOpenid(sysUser.getUserName(), password);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    private String loginOpenid(String username, String password) {
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        StudentUser studentUser = userService.selectStudentInfoByOpenId(username);
        WxUser wxUser = new WxUser();
        BeanUtils.copyBeanProp(wxUser,studentUser);
        loginUser.setWxUser(wxUser);
        loginUser.setStudentUser(studentUser);
        // 生成token
        return tokenService.createToken(loginUser);
    }


    /**
     * 登录前置校验
     *
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password) {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // IP黑名单校验
        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
            throw new BlackListException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }

}
